﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Collections.Specialized;
using System.ComponentModel;

namespace Pavilion.DesignerTool
{
    public class AnimationData : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        public string Name { get { return Animation.Name; } set { SetName(value); } }
        public Animation Animation { get; set; }
        public NotifiableCollection<FrameData> FrameDataCollection { get; private set; }
        public UndoRedoController UndoRedoController { get; private set; }
        public ResourceData Parent { get; private set; }

        private List<FrameData> OriginalFrameList { get; set; }
        private string OriginalName { get; set; }

        public AnimationData(Animation animation, ResourceData parent)
        {
            this.Animation = animation;
            this.Parent = parent;
            FrameDataCollection = new NotifiableCollection<FrameData>();
            OriginalName = animation.Name;

            foreach (Frame frame in Animation.FrameList)
                FrameDataCollection.Add(new FrameData(frame, this));

            UndoRedoController = new UndoRedoCollection<FrameData>(FrameDataCollection);
            OriginalFrameList = new List<FrameData>(FrameDataCollection);

            FrameDataCollection.Added += (FrameData frameData, int index) =>
            {
                this.Animation.FrameList.Insert(index, frameData.Frame);
                FrameTransfer.Insert(frameData);
            };
            FrameDataCollection.Removed += (FrameData frameData, int index) =>
            {
                this.Animation.FrameList.RemoveAt(index);
                FrameTransfer.Remove(this, index);
            };
            FrameDataCollection.Moved += (int oldIndex, int newIndex) =>
            {
                this.Animation.FrameList.Move(oldIndex, newIndex);
                FrameTransfer.Reorder(this, oldIndex, newIndex);
            };
        }

        public void Dispose()
        {
            Animation.Dispose();
        }

        public void AddNewFrameData()
        {
            AddNewFrameData(Animation.CreateDefaultFrame());
        }

        public void AddNewFrameData(Frame frame)
        {
            FrameDataCollection.Add(new FrameData(frame, this));
        }

        public void PasteNewFrameData(Frame frame)
        {
            FrameData frameData = new FrameData(frame, this);
            frameData.ClearSaveTrackingPoint();
            FrameDataCollection.Add(frameData);
        }

        public void TransferData()
        {
            if (OriginalName != Name)
                AnimationTransfer.Rename(Parent.ResourceName, OriginalName, Name);

            for (int i = 0; i < FrameDataCollection.Count; i++)
            {
                if (IsFrameDifferentIndex(i))
                {
                    int originalIndex = OriginalFrameList.IndexOf(FrameDataCollection[i]);

                    if (originalIndex != -1)
                        FrameTransfer.Reorder(this, originalIndex, i);
                    else
                        FrameTransfer.Insert(FrameDataCollection[i]);
                }
            }

            for (int i = FrameDataCollection.Count; i < OriginalFrameList.Count; i++)
                FrameTransfer.Remove(this, i);

            foreach (FrameData frameData in FrameDataCollection)
                frameData.TransferData();
        }

        public void Save(string resourceRawDataPath, string fullResourcePath)
        {
            string fullAnimationRawDataPath = resourceRawDataPath + Animation.Name;
            PrepareAnimationForSaving(fullAnimationRawDataPath, fullResourcePath);

            int count = 0;

            foreach (FrameData frameData in FrameDataCollection)
                frameData.Save(fullAnimationRawDataPath + @"\" + count++);
        }

        private void PrepareAnimationForSaving(string fullAnimationRawDataPath, string fullResourcePath)
        {
            if (IsAnimationChanged)
                UpdateCurrentDirectory(fullAnimationRawDataPath, fullResourcePath);

            DeleteAllRemovedFrames(fullAnimationRawDataPath);
            DeleteAllDifferentIndexFrames(fullAnimationRawDataPath);

            ResetSaveTrackingPoint();
        }

        private void ResetSaveTrackingPoint()
        {
            OriginalFrameList.Clear();
            OriginalFrameList.AddRange(FrameDataCollection);
            OriginalName = Animation.Name;
        }

        private void UpdateCurrentDirectory(string fullAnimationRawDataPath, string fullResourcePath)
        {
            string originalSavePath = fullResourcePath + @"Raw Data\" + OriginalName;

            if (OriginalName != Animation.Name)
                try { Directory.Move(originalSavePath, fullAnimationRawDataPath); } catch { }
            
            if (!Directory.Exists(fullAnimationRawDataPath))
                Directory.CreateDirectory(fullAnimationRawDataPath);
        }

        private void DeleteAllDifferentIndexFrames(string fullAnimationRawDataPath)
        {
            for (int i = 0; i < FrameDataCollection.Count; i++)
            {
                if (IsFrameDifferentIndex(i) && !IsFrameNew(i))
                    Directory.Delete(fullAnimationRawDataPath + @"\" + OriginalFrameList.IndexOf(FrameDataCollection[i]).ToString(), true);
            }
        }

        private void DeleteAllRemovedFrames(string fullAnimationRawDataPath)
        {
            foreach (FrameData frameData in OriginalFrameList)
            {
                if (!FrameDataCollection.Contains(frameData))
                    Directory.Delete(fullAnimationRawDataPath + @"\" + OriginalFrameList.IndexOf(frameData).ToString(), true);
            }
        }

        private bool IsFrameDifferentIndex(int frameIndex) 
        {
            return frameIndex >= OriginalFrameList.Count || FrameDataCollection[frameIndex] != OriginalFrameList[frameIndex];
        }

        private bool IsFrameNew(int frameIndex) { return !OriginalFrameList.Contains(FrameDataCollection[frameIndex]); }

        public bool IsAnimationChanged
        {
            get
            {
                if (Animation.Name != OriginalName)
                    return true;

                return Enumerable.SequenceEqual<FrameData>(FrameDataCollection, OriginalFrameList);
            }
        }

        private void SetName(string value)
        {
            if (Animation.Name == value)
                return;

            string oldName = Animation.Name;
            Animation.Name = value;

            AnimationTransfer.Rename(Parent.ResourceName, oldName, value);

            OnPropertyChanged("Name");
        }

        public override string ToString()
        {
            return Animation.ToString();
        }

        public void OnPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}
